﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace SudokuWinForms
{
    public partial class Sudoku : Form
    {
        private Grid grid = new Grid();

        public Sudoku()
        {
            InitializeComponent();
            foreach (var i in Enumerable.Range(0, 9))
                gridView.Rows.Add();
            UpdateGridView();
        }

        private void UpdateGridView()
        {
            foreach (int row in Enumerable.Range(0, 9))
            {
                foreach (int col in Enumerable.Range(0, 9))
                {
                    var vals = grid[row, col];
                    var cell = gridView.Rows[row].Cells[col];

                    if (vals.Count == 1)
                    {
                        cell.Value = vals[0].ToString();
                        cell.Style.BackColor = Color.White;
                        cell.Style.ForeColor = Color.Black;
                    }
                    else if (vals.Count == 0)
                    {
                        cell.Value = "X";
                        cell.Style.BackColor = Color.Red;
                        cell.Style.ForeColor = Color.White;
                    }
                    else
                    {
                        cell.Value = vals.Count.ToString();
                        cell.Style.BackColor = Color.Beige;
                        cell.Style.ForeColor = Color.Black;
                    }
                }
            }

            label1.Text = "Cells Decided: " + grid.cells.SelectMany(x => x).Count(x => x.Count == 1);
        }

        private void reduceButton_Click(object sender, EventArgs e)
        {
            grid.Reduce();
            UpdateGridView();
        }

        private void deduceButton_Click(object sender, EventArgs e)
        {
            grid.Deduce();
            UpdateGridView();
        }

        private void gridView_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (gridView.SelectedCells.Count == 1)
            {
                if (e.KeyChar >= 'a' && e.KeyChar <= 'i')
                {
                    var ch = Char.ToUpper(e.KeyChar);
                    var cell = gridView.SelectedCells[0];
                    grid.Set(cell.RowIndex, cell.ColumnIndex, ch);
                }
                else if (e.KeyChar >= '1' && e.KeyChar <= '9')
                {
                    var ch = Char.ToUpper((char) (e.KeyChar + 'a' - '1'));
                    var cell = gridView.SelectedCells[0];
                    grid.Set(cell.RowIndex, cell.ColumnIndex, ch);
                }
                else if (e.KeyChar == 'z')
                {
                    var cell = gridView.SelectedCells[0];
                    grid.Reset(cell.RowIndex, cell.ColumnIndex);
                }

                UpdateGridView();
            }
        }

        private void validateButton_Click(object sender, EventArgs e)
        {
            label2.Text = grid.Validate() ? "Valid" : "NOT VALID";
        }

        private void gridView_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
        {
            var vals = grid[e.RowIndex, e.ColumnIndex];

            if (vals.Count > 1)
                label2.Text = String.Join(", ", vals);
        }

        private void gridView_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
        {
            label2.Text = "";
        }

        private void loadButton_Click(object sender, EventArgs e)
        {
            var dialog = new OpenFileDialog();

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                var fileData = File.ReadAllLines(dialog.FileName);
                grid = new Grid();

                foreach (var row in Enumerable.Range(0, 9))
                {
                    foreach (var col in Enumerable.Range(0, 9))
                    {
                        var ch = Char.ToUpper(fileData[row][col]);

                        if (ch >= 'A' && ch <= 'Z')
                        {
                            grid.Set(row, col, ch);
                        }
                    }
                }
            }

            UpdateGridView();
        }

        private void saveButton_Click(object sender, EventArgs e)
        {
            var dialog = new SaveFileDialog();
            
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                var fileData = grid.cells.Select(row => new String(row.Select(cell =>
                    cell.Count == 1 ? cell.First() : '_'
                ).ToArray())).ToArray();

                File.WriteAllLines(dialog.FileName, fileData);
            }
        }

        private void branchButton_Click(object sender, EventArgs e)
        {
            var branchGroup = grid.ChooseBranch();
            if (branchGroup == null) return;
            var pos = branchGroup.First();
            var permutations = Permute(grid.cells[pos.Row][pos.Col]);

            foreach (var perm in permutations)
            {
                var newGrid = grid.Copy();

                foreach (var posVal in branchGroup.Zip(perm, (x, y) => Tuple.Create(x, y)))
                {
                    var p = posVal.Item1;
                    var ch = posVal.Item2;
                    newGrid.Set(p.Row, p.Col, ch);
                }

                var window = new Sudoku();
                window.grid = newGrid;
                window.Show();
                window.UpdateGridView();
            }
        }

        private List<List<char>> Permute(List<char> values)
        {
            var possibilities = values.Select(x => new List<char>() { x }).ToList();

            foreach (var i in Enumerable.Range(0, Factorial(values.Count - 1)))
            {
                var currentPossibilities = possibilities.ToList();
                possibilities = new List<List<char>>();

                foreach (var possibility in currentPossibilities)
                {
                    foreach (var val in values.Except(possibility))
                    {
                        var p = possibility.ToList();
                        p.Add(val);
                        possibilities.Add(p);
                    }
                }
            }

            return possibilities;
        }

        private int Factorial(int n)
        {
            if (n == 0) return 1;
            int result = 1;
            foreach (var i in Enumerable.Range(1, n)) result *= i;
            return result;
        }

        private void repeatInferButton_Click(object sender, EventArgs e)
        {
            Grid previous = null;

            while (! grid.IsSame(previous))
            {
                previous = grid.Copy();
                grid.Reduce();
                grid.Deduce();
            }

            UpdateGridView();
        }
    }
}
